﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using Engine.Cameras;
using Engine.Math;
using Engine.Terrain;
using Microsoft.Xna.Framework;
using Engine.Objects.StaticObjects;

namespace Engine
{
    [Serializable]
    public class World : INotifyPropertyChanged
    {
        public World(Game game)
        {
            _game = game;
            StaticObjects=new List<StaticObject>();
            ProjMatrix = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.ToRadians(45.0f), game.GraphicsDevice.Viewport.AspectRatio,
                1.0f, 10000.0f);

        }

        #region Public Methods
                
        public IEnumerable<StaticObject>  GetStaticObjectsInRadiusArea(Vector3 center,float radius)
        {
            foreach (var staticObject in StaticObjects)
            {
                float dist = Vector3.Distance(staticObject.Translation, center);
                if (dist <= radius)
                {
                    yield return staticObject;
                }
            }
        }  
        public void LoadTerrain(string path)
        {
            HeightMapManager heightMapManager = new HeightMapManager();
            Terrain = heightMapManager.LoadHeightMapFromFile(path);
        }
        public virtual object DetectCollision(Vector3 position)
        {
            foreach (StaticObject staticObject in StaticObjects)
            {
                BoundingBox transformedBB = staticObject.InitialBoundingBox.Transform(staticObject.WorldMatrix);

                if (transformedBB.Contains(position) != ContainmentType.Disjoint)
                    return staticObject;
            }
            return null;
        }
        public StaticObject IntersectWithStaticObjects(Ray ray)
        {
            foreach (StaticObject staticObject in StaticObjects)
            {
                float? dist = staticObject.Interescts(ray);
                if (dist != null)
                    return staticObject;
            }
            return null;
        }
        public StaticObject IntersectWithStaticObjects(BoundingBox boundingBox)
        {
            foreach (StaticObject staticObject in StaticObjects)
            {
               if(staticObject.Interescts(boundingBox))
                   return staticObject;
            }
            return null;
        }
        public Vector3? IntersectWithTerrain(Ray ray)
        {
            var collisionInfo = Terrain.Intersects(ray);
            if(collisionInfo==null)
                return null;
                   
            return collisionInfo.Position;
        }
        public object Interescts(BoundingBox boundingBox)
        {
            StaticObject staticObject = IntersectWithStaticObjects(boundingBox);
            if(staticObject!=null)
                return staticObject;
            return null;
        }

        #endregion

        #region Private Methods     

        #endregion

        #region Properties

        public Matrix ViewMatrix { get; set; }
        public Matrix ProjMatrix { get; set; }
        public BaseCamera Camera
        {
            get { return (BaseCamera) _game.Services.GetService(typeof (BaseCamera)); }
        }
        [NonSerialized]
        private HeightMapTerrain _terrain;
        public HeightMapTerrain Terrain
        {
            get { return _terrain; }
            set { _terrain = value; }
        }        
        public List<StaticObject> StaticObjects { get; set; }

        private bool _drawSky;
        public bool DrawSky
        {
            get { return _drawSky; }
            set
            {
                _drawSky = value;
                OnPropertyChanged("DrawSky");
            }
        }

        #endregion

        #region Fields

        [NonSerialized]
        private  Game _game;
        public Game Game { get { return _game; }set { _game = value; } }

        #endregion

        #region Events
        
        [field: NonSerialized]
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propName)
        {
            if(PropertyChanged!=null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
        
        #endregion        
    }
}
